package com.ruoyi.eqmonitor.dispose.service;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.eqmonitor.dtos.OptDeviceHistoryDto;
import com.ruoyi.eqmonitor.utils.IotUtils;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.iotdb.rpc.IoTDBConnectionException;
import org.apache.iotdb.rpc.StatementExecutionException;
import org.apache.iotdb.session.SessionDataSet;
import org.apache.iotdb.session.pool.SessionDataSetWrapper;
import org.apache.iotdb.session.pool.SessionPool;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.read.common.Field;
import org.apache.iotdb.tsfile.read.common.RowRecord;
import org.opentsdb.client.bean.request.Point;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Log4j2
@Service
public class IotService {
    @Autowired
    private SessionPool sessionPool;
    public static final String ROOT_HX = "root.hx.yjbz.D";

    public void insertRecord(String deviceId, long time, List<String> measurements, List<TSDataType> types, List<Object> values) {
        try {
            log.info("iotdb数据入库：device_id:[{}], measurements:[{}], values:[{}]", deviceId, measurements, values);
            sessionPool.insertRecord(ROOT_HX+deviceId, time, measurements,types, values);
        } catch (Exception e) {
            log.error("IotDBSession insertRecord失败: deviceId={}, time={}, measurements={}, values={}, error={}",
                    deviceId, time, measurements, values, e.getMessage());
        }
    }
    public void write(List<Point> pointList, String version, String insertDeviceCode) {

        pointList.forEach(point -> {
            long time = point.getTimestamp()*1000;
            List<String> measurements = new ArrayList<>();
            measurements.add(point.getTags().get("monitorCode"));
            List<Object> values = new ArrayList<>();
            values.add(point.getValue().doubleValue());
            List<TSDataType> types = new ArrayList<>();
            types.add(TSDataType.DOUBLE);
            this.insertAlignedRecord(insertDeviceCode,time,measurements , types, values);
        });


    }
    public void insertAlignedRecord(String deviceId, long time, List<String> measurements, List<TSDataType> types,List<Object> values) {
        try {
            log.info("iotdb数据入库：device_id:[{}], measurements:[{}], values:[{}]", deviceId, measurements, values);
            log.info("time:{}", time);
            sessionPool.insertAlignedRecord(ROOT_HX+deviceId, time, measurements,types, values);
            //sessionPool.executeNonQueryStatement("flush");
        } catch (Exception e) {
            log.error("IotDBSession insertRecord失败: deviceId={}, time={}, measurements={}, values={}, error={}",
                    deviceId, time, measurements, values, e.getMessage());
        }
    }

    public JSONArray rawDataQuery(List<String> paths, LocalDateTime startTime, LocalDateTime endTime) {
        JSONArray array = new JSONArray();
        try{
            SessionDataSetWrapper wrapper = sessionPool.executeRawDataQuery(
                    paths,
                    startTime.toInstant(ZoneOffset.of("+8")).toEpochMilli(),
                    endTime.toInstant(ZoneOffset.of("+8")).toEpochMilli(),
                    6000);
            wrapper.setBatchSize(1024);
            List<String> columnNames = wrapper.getColumnNames();
            while (wrapper.hasNext()) {
                JSONObject result = new JSONObject();
                RowRecord rowRecord = wrapper.next();
                List<Field> fields =rowRecord.getFields();
                for (int i = 0; i < columnNames.size()-1; i++) {
                    result.put("val",fields.get(i)+"");
                }
                result.put("time", Instant.ofEpochMilli(rowRecord.getTimestamp()).atZone(ZoneId.systemDefault()).toLocalDateTime());
                array.add(result);
            }
        }catch (Exception e) {
            log.error(e.getMessage());
        }
        return array;
    }
    public JSONArray lastDataQuery(List<String> paths, LocalDateTime timePoint) {
        SessionDataSetWrapper wrapper = null;
        JSONArray array = new JSONArray();
        try{
            wrapper = sessionPool.executeLastDataQuery(paths,timePoint.toInstant(ZoneOffset.of("+8")).toEpochMilli(),6000);
            wrapper.setBatchSize(1024);
            List<String> columnNames = wrapper.getColumnNames();
            List<String> list = new ArrayList<>();
            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            while (wrapper.hasNext()) {
                JSONObject object = new JSONObject();
                RowRecord rowRecord = wrapper.next();
                List<Field> fields =rowRecord.getFields();
                for (int i = 0; i < columnNames.size()-1; i++) {
                    list.add(columnNames.get(i+1)+":"+fields.get(i));
                    object.put(columnNames.get(i+1),fields.get(i)+"");
                }

                object.put("time", df.format(Instant.ofEpochMilli(rowRecord.getTimestamp()).atZone(ZoneId.systemDefault()).toLocalDateTime()));
                array.add(object);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return array;
    }
    public JSONObject aggregationQuery(OptDeviceHistoryDto historyDto) {
        JSONObject finalResult = new JSONObject();
        SessionDataSetWrapper wrapper = null;
        for (String deviceCode:
             historyDto.getDeviceCodes()) {
            JSONArray result = new JSONArray();
            StringBuilder sql = new StringBuilder();
            //select max_value(0x0165) from root.hx.sz.931JSZ group by ([2023-09-07T00:00:00, 2023-09-07T23:00:00),1y)
            sql.append("select ");
            sql.append(historyDto.getAggregator());
            if(historyDto.getAggregator().equals("max")){
                sql.append("_value");
            }
            if(historyDto.getAggregator().equals("min")){
                sql.append("_value");
            }
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            sql.append("(").append(historyDto.getMonitorCode()).append(")  as d ");
            sql.append("from " + ROOT_HX )
                    .append(deviceCode)
                    .append(" group by ([")
                    .append(sdf.format(historyDto.getStartTime()))
                    .append(",")
                    .append(sdf.format(historyDto.getEndTime()))
                    .append("),");
            sql.append(historyDto.getDownsample().replace("n","mo"));
            sql.append(")");
            try {
                wrapper = sessionPool
                        .executeQueryStatement(sql.toString());
                wrapper.setBatchSize(1024);
                List<String> columnNames = wrapper.getColumnNames();
                while (wrapper.hasNext()) {
                    JSONObject object = new JSONObject();
                    RowRecord rowRecord = wrapper.next();
                    List<Field> fields = rowRecord.getFields();
                    object.put("time", rowRecord.getTimestamp());
                    object.put("val","null".equals(fields.get(0).getStringValue())?0:fields.get(0).getDoubleV());
                    result.add(object);
                }
            } catch (IoTDBConnectionException | StatementExecutionException e) {
                e.printStackTrace();
            }finally {
                sessionPool.closeResultSet(wrapper);
            }
            finalResult.put(deviceCode, result);
        }
        return finalResult;
    }

    public Double sumBySnAndCode(String deviceCode, String code, Date startTime, Date endTime) {
        double sum = 0d;
        SessionDataSetWrapper wrapper = null;
        StringBuilder sql = new StringBuilder();
        //select sum(0x0527) as s from root.hx.sz.634MLC where time >= 2023-09-11 00:00:00 and time <2023-09-11 23:00:00
        sql.append("select ");
        sql.append("sum ");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sql.append("(").append(code).append(")  as s ");
        sql.append("from " + ROOT_HX )
                .append(deviceCode)
                .append(" where time>=")
                .append(sdf.format(startTime))
                .append(" and time < ")
                .append(sdf.format(endTime));
        try {
            wrapper = sessionPool
                    .executeQueryStatement(sql.toString());
            wrapper.setBatchSize(1024);
            List<String> columnNames = wrapper.getColumnNames();
            if (wrapper.hasNext()) {
                JSONObject object = new JSONObject();
                RowRecord rowRecord = wrapper.next();
                List<Field> fields = rowRecord.getFields();
                sum = fields.get(0).getDoubleV();
            }
        } catch (IoTDBConnectionException | StatementExecutionException e) {
            e.printStackTrace();
        }finally {
            sessionPool.closeResultSet(wrapper);
            
        }
        return sum;
    }

    public double avgBySnAndCode(String deviceCode, String code, Date startTime, Date endTime) {
        double avg = 0d;
        SessionDataSetWrapper wrapper = null;
        StringBuilder sql = new StringBuilder();
        //select avg(0x0527) as s from root.hx.sz.634MLC where time >= 2023-09-11 00:00:00 and time <2023-09-11 23:00:00
        sql.append("select ");
        sql.append("avg ");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sql.append("(").append(code).append(")  as s ");
        sql.append("from " + ROOT_HX )
                .append(deviceCode)
                .append(" where time>=")
                .append(sdf.format(startTime))
                .append(" and time < ")
                .append(sdf.format(endTime));
        try {
            wrapper = sessionPool
                    .executeQueryStatement(sql.toString());
            wrapper.setBatchSize(1024);
            List<String> columnNames = wrapper.getColumnNames();
            if (wrapper.hasNext()) {
                JSONObject object = new JSONObject();
                RowRecord rowRecord = wrapper.next();
                List<Field> fields = rowRecord.getFields();
                avg = fields.get(0).getDoubleV();
            }
        } catch (IoTDBConnectionException | StatementExecutionException e) {
            e.printStackTrace();
        }finally {
            sessionPool.closeResultSet(wrapper);
            
        }
        return avg;
    }
    public Double avgBySnAndCode(String deviceCode, String code, LocalDateTime startTime, LocalDateTime endTime) {
        Double avg = 0d;
        SessionDataSetWrapper wrapper = null;
        StringBuilder sql = new StringBuilder();
        //select avg(0x0527) as s from root.hx.sz.634MLC where time >= 2023-09-11 00:00:00 and time <2023-09-11 23:00:00
        sql.append("select ");
        sql.append("avg ");
        DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        sql.append("(").append(code).append(")  as s ");
        sql.append("from " + ROOT_HX )
                .append(deviceCode)
                .append(" where time>=")
                .append(sdf.format(startTime))
                .append(" and time < ")
                .append(sdf.format(endTime));
        try {
            wrapper = sessionPool
                    .executeQueryStatement(sql.toString());
            wrapper.setBatchSize(1024);
            List<String> columnNames = wrapper.getColumnNames();
            if (wrapper.hasNext()) {
                JSONObject object = new JSONObject();
                RowRecord rowRecord = wrapper.next();
                List<Field> fields = rowRecord.getFields();
                avg = "null".equals(fields.get(0).toString())?null:fields.get(0).getDoubleV();
            }
        } catch (IoTDBConnectionException | StatementExecutionException e) {
            e.printStackTrace();
        }finally {
            sessionPool.closeResultSet(wrapper);
            
        }
        return avg;
    }
    @Resource
    private IotUtils iotUtils;
    public Double maxBySnAndCode(String deviceCode, String code, LocalDateTime startTime, LocalDateTime endTime) {
        Double max = 0d;
        SessionDataSetWrapper wrapper = null;
        StringBuilder sql = new StringBuilder();
        //select avg(0x0527) as s from root.hx.sz.634MLC where time >= 2023-09-11 00:00:00 and time <2023-09-11 23:00:00
        sql.append("select ");
        sql.append("MAX_VALUE ");
        DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        sql.append("(").append(code).append(")  as s ");
        sql.append("from " + ROOT_HX )
                .append(deviceCode)
                .append(" where time>=")
                .append(sdf.format(startTime))
                .append(" and time < ")
                .append(sdf.format(endTime));
        System.out.println(sql.toString());
        try {
            wrapper = sessionPool
                    .executeQueryStatement(sql.toString());
            wrapper.setBatchSize(1024);
            List<String> columnNames = wrapper.getColumnNames();
            if (wrapper.hasNext()) {
                JSONObject object = new JSONObject();
                RowRecord rowRecord = wrapper.next();
                List<Field> fields = rowRecord.getFields();
                max = "null".equals(fields.get(0).toString())?null:fields.get(0).getDoubleV();
            }
        } catch (IoTDBConnectionException | StatementExecutionException e) {
            e.printStackTrace();
        }finally {
            sessionPool.closeResultSet(wrapper);
            
        }
        return max;
    }
}
